﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

namespace XSerialization
{
    public static class Utilities
    {
        /// <summary>
        /// Returns true if the given Type is one of the primitive types like string, int etc.
        /// </summary>
        /// <param name="type">Class Type</param>
        /// <returns></returns>
        public static bool IsSimpleType(Type type)
        {
            Type t = Nullable.GetUnderlyingType(type) ?? type;
            if (t.IsPrimitive || t.IsEnum || t == typeof(string)) return true;
            return false;
        }

        /// <summary>
        /// Returns true if the given Types is a IList Type.
        /// </summary>
        /// <param name="type">Class Type</param>
        /// <returns></returns>
        public static bool IsListType(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>)) return true;
            return false;
        }

        /// <summary>
        /// Returns the generic Type definition of the IList Type.
        /// </summary>
        /// <param name="type">Class Type</param>
        /// <returns></returns>
        public static Type GetListType(Type type)
        {
            //if (type != typeof(IList<>)) return null;
            return type.GetGenericArguments()[0];
        }

        public static XObject GetXObjectFromElement(XElement element, string name, bool isAttribute)
        {
            if (element == null) return null;
            if (string.IsNullOrEmpty(name)) return element;
            if (isAttribute) return element.Attribute(name);

            return element.Element(name);
        }

        public static string GetXObjectValue(XObject xObject, bool isAttribute)
        {
            if (isAttribute) return ((XAttribute)xObject).Value;
            return ((XElement)xObject).Value;
        }

        public static string ConvertObjectToXValue(object value)
        {
            if (value == null) return null;

            Type t = Nullable.GetUnderlyingType(value.GetType()) ?? value.GetType();
            string xmlValue = (string)Convert.ChangeType(value, typeof(string), CultureInfo.InvariantCulture);

            if (t == typeof(bool))
            {
                if (!string.IsNullOrEmpty(xmlValue)) xmlValue = xmlValue.ToLower();
            }

            return xmlValue;
        }

        public static object ConvertXValueToObject(string value, Type targetType)
        {
            Type t = Nullable.GetUnderlyingType(targetType) ?? targetType;

            try
            {
                if (t.BaseType == typeof(Enum))
                {
                    try { return Enum.Parse(t, value); }
                    catch (ArgumentException) { return null; }
                }

                if (value == null) return null;
                if (targetType == typeof(string)) return Convert.ChangeType(value, t, CultureInfo.InvariantCulture);
                return Convert.ChangeType(value, t, CultureInfo.InvariantCulture);
            }
            catch (Exception e)
            {
                if (e is FormatException || e is OverflowException)
                {
                    Trace.TraceWarning("Can't convert xml value \"" + value + "\" to " + targetType + "!");
                    if (t == targetType) return Activator.CreateInstance(targetType);
                }
                else
                {
                    throw;
                }
            }

            return null;
        }

        public static IList<XProperty> GetXPropertiesFromType(Type type)
        {
            IList<XProperty> xProperties = new List<XProperty>();
            PropertyInfo[] properties = type.GetProperties();

            foreach (PropertyInfo propertyInfo in properties)
            {
                //get XmlBinding attribute
                object[] attributes = propertyInfo.GetCustomAttributes(typeof(IXBinding), true);
                if (attributes.Count() != 1) continue;
                IXBinding binding = attributes[0] as IXBinding;
                if (binding == null) continue;

                xProperties.Add(new XProperty { Binding = binding, Info = propertyInfo });
            }

            //order list by sequence index and name
            IEnumerable<XProperty> orderedList = xProperties.OrderBy(m => m.Binding.SequenceIndex).ThenBy(n => n.Binding.XmlName);
            xProperties = orderedList.ToList();

            return xProperties;
        }
    }

    /// <summary>
    /// Conatiner Class for XmlBinding and PropertyInfo
    /// </summary>
    public class XProperty
    {
        /// <summary>
        /// PropertyInfo
        /// </summary>
        public PropertyInfo Info { get; set; }

        /// <summary>
        /// XmlBindingInfo
        /// </summary>
        public IXBinding Binding { get; set; }
    }
}
